#include "Controller.hpp"

#include "gl.hpp"
#include <boost/bind.hpp>
#include <iostream>

#include "Camera.hpp"
#include "ControlPoint.hpp"
#include "GlWindow.hpp"
#include "MenuEvents.hpp"
#include "VertexSelector.hpp"
#include "ViewScene.hpp"
#include "MeshAlgorithm.hpp"
#include "MeshView.hpp"
#include "SelfIntersectionView.hpp"

Controller::Controller()
{
   mVertexSelector = new VertexSelector;
   mMainPanel = new MainPanel;
   mMainPanel->make_window();
   mMainPanel->mWindow->position(0,20);
   mMainPanel->mWindow->size(500,520);
   mGlWindow = new GlWindow(0,20,500,500 - mMainPanel->mStatusBar->h());
   mMainPanel->mStatusBar->position(0, 520 - mMainPanel->mStatusBar->h());
   mGlWindow->resizable(mGlWindow);
   //mMainPanel->mWindow->resizable(mMainPanel->mWindow);
   mMainPanel->mWindow->add(*mGlWindow);
   mMainPanel->mWindow->resizable(mGlWindow);
   mMainPanel->mWindow->size(800,620);
   mMainPanel->mWindow->show();
   
   mMeshPanel = new MeshPanel;
   mMeshPanel->make_window();
   mMeshPanel->mWindow->show();
   mMeshPanel->mWindow->position(mMainPanel->mWindow->w()+5,20);
   FlEx::MakeTopmost(mMeshPanel->mWindow);
   MEvents.Signal("Show Mesh Window").connect(ToggleVisibleFn<Fl_Window>(mMeshPanel->mWindow));

   mViewPanel = new ViewPanel;
   mViewPanel->make_window();
   mViewPanel->mWindow->show();
   mViewPanel->mWindow->position(mMainPanel->mWindow->w()+5,mMeshPanel->mWindow->y()+mMeshPanel->mWindow->h()+20);
   FlEx::MakeTopmost(mViewPanel->mWindow);
   MEvents.Signal("Show View Window").connect(ToggleVisibleFn<Fl_Window>(mViewPanel->mWindow));

   mEditPanel = new EditPanel;
   mEditPanel->make_window();
   mEditPanel->mWindow->show();
   mEditPanel->mWindow->position(mMainPanel->mWindow->w()+5,mViewPanel->mWindow->y()+mViewPanel->mWindow->h()+20);
   FlEx::MakeTopmost(mEditPanel->mWindow);
   MEvents.Signal("Show View Window").connect(ToggleVisibleFn<Fl_Window>(mEditPanel->mWindow));
   
   mSimplifyDlg = new SimplifySurfaceDlg;
   mSimplifyDlg->make_window();

   mPropertiesDlg = new MeshPropertiesDlg;
   mPropertiesDlg->make_window();

   mMaterialDlg = new MaterialDlg;
   mMaterialDlg->make_window();

   mRegisterDlg = new RegisterDlg;
   mRegisterDlg->make_window();
   mRegisterDlg->mIterations->value("1");
   mRegisterDlg->mFitWeight->value("40");
   mRegisterDlg->mEnergyWeight->value("1");
   mRegisterDlg->mSubLevel->value("3");
   

   mInterfaceDetatcher = new DetatchPanel;
   mIntersectingView = new SelfIntersectionView;
   //mDetatchDlg = new DetatchInterfaceDlg;
   //mDetatchDlg->make_window();

   mCamera = new Camera(&ViewScene::GetInstance().mTransform);
   //MEvents.Signal("Show View Window").connect(boost::bind(&Controller::ShowViewWindow, this));

   ViewScene::GetInstance().mTransform.mHeight = (float)mGlWindow->h();
   ViewScene::GetInstance().mTransform.mWidth = (float)mGlWindow->w();

   mGlWindow->SetController(this);

   MEvents.Signal("Exit").connect(boost::bind(&Controller::Exit, this, _1));

   mControlPoint = new ControlPoint(&mCamera->mViewPoint,this);;
}
void Controller::ShowViewWindow()
{
   if (! mViewPanel->mWindow->visible())
   {
      mViewPanel->mWindow->show();
   }else
   {
      mViewPanel->mWindow->hide();
   }
}
Controller::~Controller()
{
   delete mControlPoint;
   delete mIntersectingView;
   delete mInterfaceDetatcher;
}
Controller& Controller::GetInstance()
{
   static Controller i;
   return i;
}
void Controller::RedrawGl()
{
   mGlWindow->redraw();
}
int Controller::handle(int event)
{
   int r=0;
   r = (!r) ? mVertexSelector->handle(event) : r;
   r = (r!=1) ? mControlPoint->handle(event) : r;
   r = (r!=1) ? mCamera->handle(event) : r;
   if (r!=1)
   {
      if (event == FL_KEYDOWN)
      {
         if (Fl::event_key() == 'l')
         {
            MeshView* view = ViewScene::GetInstance().GetSelectedMesh();
            if (view)
            {
               std::list<std::pair<RegionInterface, IndexList> > cycs;
               MeshAlgorithm::FindCycles(view->mMesh, view->GetCreases(), view->GetSelectedVertices(), cycs);
               view->mMesh.UpdateFaces();
               view->UpdateMesh();
               RedrawGl();
            }
            r = 1;
         }
         else if (Fl::event_key() == 'm')
         {
            MEvents("Change Materials");
            r = 1;
         }
         else if (Fl::event_key() == 'c')
         {
            mCamera->mViewPoint.PointAt(mControlPoint->mPoint, mCamera->mViewPoint.Right(), mCamera->mViewPoint.Up());
            mCamera->SetFocusDistance((mControlPoint->mPoint - mCamera->mViewPoint.Origin()).length());
            mCamera->UpdateView();
            r = 1;
         }
         else if (Fl::event_key() == 'f')
         {
            MEvents("Create Face");
            r = 1;
         }
         else if (Fl::event_key() == 'x')
         {
            MEvents("Delete Selection");
            r = 1;
            //Fl::popup()
         }
      }
   }
   if (r)
   {
      RedrawGl();
   }
   if (r>1) r=1;
   return r;
}

void Controller::SetupLighting()
{
   glShadeModel( GL_FLAT);

   //float lightpos[4] = {0,-mModel->mMeshBounds.Max().y * 2,0,0};
   //float zero[4] = {1,1,1,1};
   float lightpos[4] = {0,-5 * 2,0,0};
   
   float ambient[4] = {.5,.5,.5,.5};
   //float diffuse[4] = {1,1,1,1};
   float diffuse[4] = {.3,.3,.3,1};

   //glLightModeli ( GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE );
   glLightfv ( GL_LIGHT0, GL_POSITION, lightpos );
   glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
   glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
   glLightfv( GL_LIGHT0, GL_SPECULAR, diffuse );
   glEnable( GL_LIGHT0 );

   //float lightpos2[4] = {5*2,0,0,0};
   //glLightfv ( GL_LIGHT1, GL_POSITION, lightpos2 );
   //glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
   //glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse );
   //glLightfv( GL_LIGHT1, GL_SPECULAR, diffuse );
   //glEnable( GL_LIGHT1 );

   glEnable(GL_LIGHTING);

   float materialspec[4] = { 0.15f, 0.15f, 0.15f, .10f } ;
   glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, materialspec ) ;
   glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 20 ) ;
   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );

}
void Controller::Draw()
{
   glEnable(GL_CULL_FACE);
   glCullFace(GL_BACK);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   ViewTransform& transform = ViewScene::GetInstance().mTransform;
   transform.mWidth = (float)mGlWindow->w();
   transform.mHeight = (float)mGlWindow->h();

   transform.mProjection = math::perspective(45,transform.mWidth / transform.mHeight,
      transform.mNearPlane,transform.mFarPlane);
   gl::Multiply(transform.mProjection);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   SetupLighting();
   glClearColor(.35,.35,.35, 0);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   gl::Multiply(transform.GetTransform());
   ViewScene::GetInstance().Draw();

   mCamera->Draw();
   
   //mScene->Draw();
   //DrawSelVerts();

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0, mGlWindow->w(), mGlWindow->h(), 0, 0,1);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();

   
   //mScene->DrawWindow();
   mVertexSelector->DrawWindow();
   mControlPoint->DrawWindow();

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gl::Multiply(transform.mProjection);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gl::Multiply(transform.GetTransform());

   mControlPoint->Draw();
   gl::AssertGlError();
   mIntersectingView->Draw();
   gl::AssertGlError();
   //glClearColor(1,1,1,0);
   //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   //mCamera->Draw();
}
void Controller::Exit(const boost::many& args)
{
   mMainPanel->mWindow->hide();
   mMeshPanel->mWindow->hide();
   mViewPanel->mWindow->hide();
   mEditPanel->mWindow->hide();
}

void Controller::SetStatus(const std::string& aStatus)
{
   mMainPanel->mStatusBar->copy_label(aStatus.c_str());
   mMainPanel->mStatusBar->redraw();
}
